package com.example.demo.util.aspose;

import cn.hutool.core.io.IoUtil;
import com.aspose.words.*;
import com.aspose.words.net.System.Data.DataRow;
import com.aspose.words.net.System.Data.DataTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * Created by zyc(378661553@qq.com) on 2021/3/30 14:37
 */
public class AsposeWordUtils {

    private static Logger log = LoggerFactory.getLogger(AsposeWordUtils.class);

    static {
        init();
    }

    private static void init() {
        InputStream is = null;
        try {
            String licenseStr = "<License><Data><Products><Product>Aspose.Total for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>";
            log.info("开始加载证书信息");
            is = new ByteArrayInputStream(licenseStr.getBytes());
            License license = new License();
            license.setLicense(is);
            log.info("加载证书信息完毕");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("自动加载aspose证书文件失败");
            throw new IllegalArgumentException("自动加载aspose证书文件失败!", e);
        } finally {
            IoUtil.close(is);
        }
    }

    public static void main(String[] args) throws FileNotFoundException {
        Map<String,Object> model = new HashMap<>();

        HtmlTextBo htmlTextBo = new HtmlTextBo();

        model.put("html",htmlTextBo);
        String in = "C:/Users/dell/Documents/WeChat Files/wxid_g7ri3vmqxpt022/FileStorage/File/2022-01/无人机管控平台二期重构详细设计方案.docx";
        String out = "E:/无人机管控平台二期重构详细设计方案.docx";
        asposeWords(in,out,model);


    }

    public static void asposeWords(String templatePath, String osPath, Map<String, Object> model) throws FileNotFoundException {
        FileInputStream  template = null;
        FileOutputStream os       = null;
        try {
            template = new FileInputStream(templatePath);
            os = new FileOutputStream(osPath);
            asposeWords(template, os, model, WordType.docx, Throwable::printStackTrace);
        } finally {
            IoUtil.close(os);
            IoUtil.close(template);
        }
    }

    public static void asposeWords(InputStream template, OutputStream os, Map<String, Object> model) {
        asposeWords(template, os, model, WordType.docx, Throwable::printStackTrace);
    }

    public static void asposeWords(InputStream template, OutputStream os, Map<String, Object> model, WordType wordType, Consumer<Exception> exceptionPloy) {
        Assert.notNull(template, "模板文件流不能为空");
        Assert.notNull(os, "输出流不能为空");
        Assert.notNull(model, "数据model不能为空");
        try {
            Document        word       = new Document(template);
            DocumentBuilder builder    = new DocumentBuilder(word);
            List<String>    fieldNames = new ArrayList<>(model.size());
            List<Object>    values     = new ArrayList<>(model.size());
            model.forEach((k, v) -> {
                handelData(word, builder, fieldNames, values, k, v, exceptionPloy);
            });
            word.getMailMerge().execute(fieldNames.toArray(new String[0]), values.toArray());
            word.save(os, SaveOptions.createSaveOptions(wordType.getSaveFormat()));
        } catch (Exception e) {
            exceptionPloy.accept(e);
        }
    }

    private static void handelData(Document word, DocumentBuilder builder, List<String> fieldNames, List<Object> values,
                                   String k, Object v, Consumer<Exception> exceptionPloy) {
        if (v == null) {
            fieldNames.add(k);
            values.add(null);
            return;
        }
        if (v instanceof Collection) {
            // 表格相关的数据，需要通过遍历的方式进行
            fillCollectionData(word, builder, k, (Collection)v, exceptionPloy);
        } else if (v instanceof BufferedImage) {
            // 单个图片文件
            try {
                builder.moveToMergeField(k);
                builder.insertImage((BufferedImage)v);
            } catch (Exception e) {
                exceptionPloy.accept(e);
            }
        } else if (v instanceof ImageBo) {
            ImageBo ib = (ImageBo)v;
            try {
                builder.moveToMergeField(k);
            } catch (Exception e) {
                exceptionPloy.accept(e);
            }
            insertImageBo(builder, ib, exceptionPloy);
        } else if (v instanceof HtmlTextBo) {
            HtmlTextBo vb = (HtmlTextBo)v;
            try {
                builder.moveToMergeField(k);
                if (vb.getUseBuilderFormatting() == null) {
                    builder.insertHtml(vb.getHtml());
                } else {
                    builder.insertHtml(vb.getHtml(), vb.getUseBuilderFormatting());
                }
            } catch (Exception e) {
                exceptionPloy.accept(e);
            }
        } else {
            fieldNames.add(k);
            values.add(v);
        }
    }

    private static void insertImageBo(DocumentBuilder builder, ImageBo ib, Consumer<Exception> exceptionPloy) {
        InputStream image = null;
        try {
            image = new URL(ib.getProtocolPath()).openStream();
            ImageBo.Size s = ib.getSize();
            if (s != null) {
                ImageBo.Position p = ib.getPosition();
                if (p != null) {
                    builder.insertImage(image, p.getHorzPos(), p.getLeft(), p.getVertPos(), p.getTop(), s.getWidth(), s.getHeight(), p.getWrapType());
                } else {
                    builder.insertImage(image, s.getWidth(), s.getHeight());
                }
            } else {
                builder.insertImage(image);
            }
        } catch (Exception e) {
            exceptionPloy.accept(e);
        } finally {
            IoUtil.close(image);
        }
    }

    @SuppressWarnings("ALL")
    private static void fillCollectionData(Document word, DocumentBuilder builder, String key, Collection collection, Consumer<Exception> exceptionPloy) {
        final String       tableName    = key;
        DataTable          dataTable    = new DataTable(tableName);
        AtomicInteger      rowIndexFlag = new AtomicInteger(0);
        List<TableImageBo> imageProcess = new ArrayList<>();
        for (Object item : collection) {
            // 由于一般都存在一行表头，所以这里我们是获取的自增后的值
            int baseRowIndex = rowIndexFlag.incrementAndGet();
            // 创建行
            DataRow dataRow = dataTable.newRow();
            // 添加行到 dataTable 中
            dataTable.getRows().add(dataRow);

            BiConsumer<Object, Object> bic = (ik, iv) -> {
                if (iv instanceof TableImageBo) {
                    if (((TableImageBo)iv).getRowIndex() == null) {
                        ((TableImageBo)iv).setRowIndex(baseRowIndex);
                    }
                    imageProcess.add((TableImageBo)iv);
                } else {
                    String ikStr = ik.toString();
                    if (!dataTable.getColumns().contains(ikStr)) {
                        dataTable.getColumns().add(ikStr);
                    }
                    dataRow.set(ikStr, iv);
                }
            };

            if (item instanceof Map) {
                ((Map)item).forEach(bic);
            } else {
                Map<String, Object> m = new HashMap<>();
                Class<?>            c = item.getClass();
                while (true) {
                    Field[] dfs = c.getDeclaredFields();// 获取该对象的所有字段(不包含父类的字段)
                    for (int i = 0; i < dfs.length; i++) {
                        Field  df = dfs[i];
                        String ik = df.getName();
                        Object iv = null;
                        try {
                            df.setAccessible(true);
                            iv = df.get(item);
                        } catch (IllegalAccessException e) {
                            exceptionPloy.accept(e);
                        }
                        if (!m.containsKey(ik)) {
                            m.put(ik, iv);
                        }
                    }
                    c = c.getSuperclass();
                    if (c == null) {
                        break;
                    }
                }
                m.forEach(bic);
            }
        }
        try {
            word.getMailMerge().executeWithRegions(dataTable);
            imageProcess.forEach(iv -> {
                builder.moveToCell(iv.getTableIndex(), iv.getRowIndex(), iv.getColumnIndex(), 0);
                insertImageBo(builder, iv.getImage(), exceptionPloy);
            });
        } catch (Exception e) {
            exceptionPloy.accept(e);
        }
    }
}
